package com.atguigu.tingshu.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.account.client.UserAccountFeignClient;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.TrackInfoFeignClient;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.user.client.VipServiceConfigFeignClient;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderDerateMapper orderDerateMapper;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;

    @Autowired
    private VipServiceConfigFeignClient vipServiceConfigFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private TrackInfoFeignClient trackInfoFeignClient;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private UserAccountFeignClient userAccountFeignClient;


    @Override
    public void cancelOrder(String orderNo) {
        //  利用业务字段防止消息重复消费!
        OrderInfo orderInfo = this.getOrderInfoByOrderNo(orderNo);
        //  判断当前订单的状态：
        if (orderInfo.getOrderStatus().equals(SystemConstant.ORDER_STATUS_UNPAID)) {
            //  修改订单状态：
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
            //  取消订单的本质：修改订单状态;
            orderInfoMapper.updateById(orderInfo);
        }
        //  取消订单只是其中一部分; 再讲更多的取消订单内容！

    }

    @Override
    public void orderPaySuccess(String orderNo) {
        //  为了保证幂等性，先查询订单对象；判断订单是否已更新;
        OrderInfo orderInfo = this.getOrderInfoByOrderNo(orderNo);
        if (orderInfo.getOrderStatus().equals(SystemConstant.ORDER_STATUS_PAID)) {
            return;
        }
        //  更新状态;
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
        //  执行sql;
        this.updateById(orderInfo);
        //  保存用户购买记录;
        UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
        userPaidRecordVo.setUserId(orderInfo.getUserId());
        userPaidRecordVo.setItemType(orderInfo.getItemType());
        userPaidRecordVo.setOrderNo(orderNo);
        //  去哪里找购买项目的Id; 获取 orderInfoVo.orderDetailVoList()下的itemId就可以了.
        userPaidRecordVo.setItemIdList(orderInfo.getOrderDetailList().stream().map(OrderDetail::getItemId).collect(Collectors.toList()));
        //  远程调用！
        Result userResult = userInfoFeignClient.savePaidRecord(userPaidRecordVo);
        if (200 != userResult.getCode()) {
            //  扣减金额失败;
            throw new GuiguException(userResult.getCode(), "保存用户购买记录失败");
        }
    }

    @Override
    public IPage<OrderInfo> findUserPage(Page<OrderInfo> orderInfoPage, String orderStatus, Long userId) {
        //  调用mapper层方法.
        IPage<OrderInfo> iPage = orderInfoMapper.selectUserPage(orderInfoPage, orderStatus, userId);
        //  赋值订单状态名称;
        //  赋值支付名称;
        iPage.getRecords().forEach(orderInfo -> {
            orderInfo.setPayWayName(SystemConstant.ORDER_PAY_ACCOUNT.equals(orderInfo.getPayWay()) ? "余额" : "在线");
            //  赋值订单状态名称; order_status 0901=未支付;0902=已支付;0903=已取消;
            String orderStatusName = SystemConstant.ORDER_STATUS_UNPAID.equals(orderInfo.getOrderStatus()) ? "未支付" :
                    SystemConstant.ORDER_STATUS_PAID.equals(orderInfo.getOrderStatus()) ? "已支付" :
                            SystemConstant.ORDER_STATUS_CANCEL.equals(orderInfo.getOrderStatus()) ? "已取消" : "";
            orderInfo.setOrderStatusName(orderStatusName);
        });
        return iPage;
    }

    /**
     * 根据订单编号查询订单对象
     *
     * @param orderNo
     * @return
     */
    public OrderInfo getOrderInfoByOrderNo(String orderNo) {
        //  查询订单对象;
        OrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<OrderInfo>().eq(OrderInfo::getOrderNo, orderNo));
        //  给支付方式赋值：
        if (null != orderInfo) {
            //  通过这个字段pay_way 赋值名称; orderInfo.getPayWay()=1101 1102 1103;
            orderInfo.setPayWayName(SystemConstant.ORDER_PAY_ACCOUNT.equals(orderInfo.getPayWay()) ? "余额" : "在线");
            //  获取订单明细;
            orderInfo.setOrderDetailList(orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>().eq(OrderDetail::getOrderId, orderInfo.getId())));
        }
        //  查询订单对象;
        return orderInfo;
    }

    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public String submitOrder(OrderInfoVo orderInfoVo, Long userId) {
        //  1.  校验签名;
        Map map = JSON.parseObject(JSON.toJSONString(orderInfoVo), Map.class);
        //  页面传递的数据;orderInfoVo.getPayWay有值的,而在页面提交的时候是“”;
        map.put("payWay", "");
        //  需要将map做为参数传递给方法.
        SignHelper.checkSign(map);
        //  2.  防止重复提交订单
        //  页面的流水号;
        String tradeNo = orderInfoVo.getTradeNo();
        //  防止重复提交订单;
        String orderKey = userId + tradeNo;
        String orderNo = null;
        try {
            //  利用setnx + setex: 分布式锁思想！
            Boolean flag = this.redisTemplate.opsForValue().setIfAbsent(orderKey, 1, 1, TimeUnit.MINUTES);
            if (!flag) {
                throw new GuiguException(811, "重复提交订单");
            }
            //  定义一个orderNo
            orderNo = UUID.randomUUID().toString().replaceAll("-", "");
            //  3.  现在选择哪种支付方式！
            if (!SystemConstant.ORDER_PAY_ACCOUNT.equals(orderInfoVo.getPayWay())) {
                //  在线支付：
                this.saveOrder(orderInfoVo, userId, orderNo);
                //  发送延迟消息实现取消订单;
                this.rabbitService.sendDealyMessage(MqConst.EXCHANGE_CANCEL_ORDER, MqConst.ROUTING_CANCEL_ORDER, orderNo, MqConst.CANCEL_ORDER_DELAY_TIME);
            } else {
                //  余额支付;
                //  1.  扣减余额; 在account账户微服务！
                //  需要将扣减的数据以及给谁扣减封装到一个实体类中;
                AccountDeductVo accountDeductVo = new AccountDeductVo();
                accountDeductVo.setUserId(userId);
                accountDeductVo.setAmount(orderInfoVo.getOrderAmount());
                accountDeductVo.setContent("订单号：" + orderNo);
                accountDeductVo.setOrderNo(orderNo);
                //  获取远程调用的结果;
                Result result = userAccountFeignClient.checkAndDeduct(accountDeductVo);
                if (200 != result.getCode()) {
                    //  扣减金额失败;
                    throw new GuiguException(result.getCode(), "扣减金额失败");
                }
                //  2.  保存订单;
                this.saveOrder(orderInfoVo, userId, orderNo);
                //  修改订单状态;
                OrderInfo orderInfo = this.getOrderInfoByOrderNo(orderNo);
                orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
                orderInfoMapper.updateById(orderInfo);
                //  3.  记录用户购买信息;
                //  创建一个对象;来封装保存的信息;
                UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
                userPaidRecordVo.setUserId(userId);
                userPaidRecordVo.setItemType(orderInfoVo.getItemType());
                userPaidRecordVo.setOrderNo(orderNo);
                //  去哪里找购买项目的Id; 获取 orderInfoVo.orderDetailVoList()下的itemId就可以了.
                userPaidRecordVo.setItemIdList(orderInfoVo.getOrderDetailVoList().stream().map(OrderDetailVo::getItemId).collect(Collectors.toList()));
                //  远程调用！
                Result userResult = userInfoFeignClient.savePaidRecord(userPaidRecordVo);
                if (200 != userResult.getCode()) {
                    //  扣减金额失败;
                    throw new GuiguException(userResult.getCode(), "保存用户购买记录失败");
                }
            }
        } catch (GuiguException e) {
            //  记录日志信息;
            System.out.println(e.getMessage());
            throw new RuntimeException(e);
        }
        //  返回订单编号；
        return orderNo;
    }

    /**
     * @param orderInfoVo
     * @param userId
     * @param orderNo
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveOrder(OrderInfoVo orderInfoVo, Long userId, String orderNo) {
        /*
            order_derate
            order_detail
            order_info
         */
        OrderInfo orderInfo = new OrderInfo();
        //  属性拷贝：
        BeanUtils.copyProperties(orderInfoVo, orderInfo);
        orderInfo.setUserId(userId);
        orderInfo.setOrderTitle(orderInfoVo.getOrderDetailVoList().get(0).getItemName());
        orderInfo.setOrderNo(orderNo);
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
        orderInfoMapper.insert(orderInfo);
        //  order_detail
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        if (!CollectionUtils.isEmpty(orderDetailVoList)) {
            //  循环遍历;
            for (OrderDetailVo orderDetailVo : orderDetailVoList) {
                //  创建对象;
                OrderDetail orderDetail = new OrderDetail();
                //  属性拷贝;
                BeanUtils.copyProperties(orderDetailVo, orderDetail);
                orderDetail.setOrderId(orderInfo.getId());
                //  保存;
                orderDetailMapper.insert(orderDetail);
            }
        }

        //  order_derate
        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
        if (!CollectionUtils.isEmpty(orderDerateVoList)) {
            //  循环遍历保存数据
            for (OrderDerateVo orderDerateVo : orderDerateVoList) {
                //  创建对象
                OrderDerate orderDerate = new OrderDerate();
                //  属性拷贝;
                BeanUtils.copyProperties(orderDerateVo, orderDerate);
                orderDerate.setOrderId(orderInfo.getId());
                //  保存数据;
                orderDerateMapper.insert(orderDerate);
            }
        }
    }

    @Override
    public OrderInfoVo trade(TradeVo tradeVo, Long userId) {
        //  实现类本质：给 OrderInfoVo 属性赋值！
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        //  声明字段接收金额数据：
        BigDecimal originalAmount = new BigDecimal("0.00");
        //  折扣金额；
        BigDecimal derateAmount = new BigDecimal("0.00");
        //  订单实际金额;
        BigDecimal orderAmount = new BigDecimal("0.00");
        //  声明集合来存储订单明细;
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        //  声明集合来存储减免明细
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();
        //  根据用户Id 查询用户信息;
        Result<UserInfoVo> infoVoResult = userInfoFeignClient.getUserInfo(userId);
        Assert.notNull(infoVoResult, "用户信息结果集不存在！");
        UserInfoVo userInfoVo = infoVoResult.getData();
        Assert.notNull(userInfoVo, "用户信息不存在！");
        //  属性赋值：
        if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(tradeVo.getItemType())) {
            //  购买专辑;
            //  1.  判断用户是否购买过专辑;user_paid_album; user_id and album_id;
            Result<Boolean> booleanResult = userInfoFeignClient.isPaidAlbum(tradeVo.getItemId());
            Assert.notNull(booleanResult, "查询用户是否购买过专辑失败！");
            if (booleanResult.getData()) {
                //  抛出异常；
                throw new GuiguException(810, "用户已经购买过专辑，不能重复购买！");
            }
            //  2.  根据专辑Id获取专辑对象
            Result<AlbumInfo> infoResult = albumInfoFeignClient.getAlbumInfo(tradeVo.getItemId());
            Assert.notNull(infoResult, "专辑结果集不存在！");
            AlbumInfo albumInfo = infoResult.getData();
            Assert.notNull(albumInfo, "专辑对象不存在！");
            //  原始金额
            originalAmount = albumInfo.getPrice();
            //  未购买过！ 判断用户的身份！ 在判断用户购买的商品是否有折扣！
            if (0 == userInfoVo.getIsVip() || (1 == userInfoVo.getIsVip() && userInfoVo.getVipExpireTime().before(new Date()))) {
                //  你是普通用户
                //  判断是否有折扣; albumInfo.getDiscount().compareTo(new BigDecimal("-1")) = 1: 有折扣
                if (albumInfo.getDiscount().compareTo(new BigDecimal("-1")) > 0) {
                    //  计算折扣的金额; 1000 8; 折扣金额：200；实际金额 800;  1000; 1000*(10-8)/10=200;
                    derateAmount = originalAmount.multiply(new BigDecimal("10").subtract(albumInfo.getDiscount())).divide(new BigDecimal("10"), 2, BigDecimal.ROUND_HALF_UP);
                }
            } else {
                //  是vip 用户!
                //  判断是否有折扣;
                if (albumInfo.getVipDiscount().compareTo(new BigDecimal("-1")) > 0) {
                    //  计算折扣的金额; 1000 8; 折扣金额：200；实际金额 800;  1000; 1000*(10-8)/10=200;
                    derateAmount = originalAmount.multiply(new BigDecimal("10").subtract(albumInfo.getVipDiscount())).divide(new BigDecimal("10"), 2, BigDecimal.ROUND_HALF_UP);
                }
            }
            //  实际金额;
            orderAmount = originalAmount.subtract(derateAmount);
            //  订单明细：
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemName(albumInfo.getAlbumTitle());
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
            orderDetailVo.setItemPrice(originalAmount);
            orderDetailVoList.add(orderDetailVo);
            //  减免明细：
            //  判断折扣金额大于0,再将减免对象添加到集合中;
            if (derateAmount.compareTo(new BigDecimal("0.00")) == 1) {
                //  创建折扣对象;
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT);
                orderDerateVo.setRemarks("专辑折扣");
                orderDerateVoList.add(orderDerateVo);
            }

        } else if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(tradeVo.getItemType())) {
            //  购买声音；
            Integer trackCount = tradeVo.getTrackCount();
            //  trackCount必须大于等于0 0：表示购买本集;
            if (trackCount < 0) {
                throw new GuiguException(810, "购买声音数量不能小于0！");
            }
            //  获取订单明细集合，循环遍历赋值订单明细对象！
            //  传递声音Id，count购买的集数
            Result<List<TrackInfo>> trackInfoListResult = trackInfoFeignClient.findPaidTrackInfoList(tradeVo.getItemId(), tradeVo.getTrackCount());
            Assert.notNull(trackInfoListResult, "查询声音结果集不存在！");
            List<TrackInfo> trackInfoList = trackInfoListResult.getData();
            Assert.notNull(trackInfoList, "查询声音对象不存在！");
            //  获取到专辑对象；
            Result<AlbumInfo> infoResult = albumInfoFeignClient.getAlbumInfo(trackInfoList.get(0).getAlbumId());
            Assert.notNull(infoResult, "查询专辑结果集不存在！");
            AlbumInfo albumInfo = infoResult.getData();
            Assert.notNull(albumInfo, "查询专辑对象不存在！");
            //  原始金额：单价*数量; 专辑类型是0201; price: 单集声音的价格; 现在需要通过已知条件找到专辑对象; .price();
            originalAmount = 0 == trackCount ? albumInfo.getPrice() : albumInfo.getPrice().multiply(new BigDecimal(trackCount));
            //  声音没有打折这一说！
            orderAmount = originalAmount;
            //  订单明细没有写; 如何去判断购买明细的条数?
            for (TrackInfo info : trackInfoList) {
                OrderDetailVo orderDetailVo = new OrderDetailVo();
                orderDetailVo.setItemId(info.getId());
                orderDetailVo.setItemName(info.getTrackTitle());
                orderDetailVo.setItemUrl(info.getCoverUrl());
                //  声音的单价=专辑的单价;
                orderDetailVo.setItemPrice(albumInfo.getPrice());
                orderDetailVoList.add(orderDetailVo);
            }
        } else {
            //  购买vip; vip_service_config
            Result<VipServiceConfig> vipServiceConfigResult = vipServiceConfigFeignClient.getVipServiceConfig(tradeVo.getItemId());
            //  判断
            Assert.notNull(vipServiceConfigResult, "查询vip服务失败！");
            VipServiceConfig vipServiceConfig = vipServiceConfigResult.getData();
            Assert.notNull(vipServiceConfig, "查询vip服务失败！");
            //  原价
            originalAmount = vipServiceConfig.getPrice();
            //  折扣
            derateAmount = originalAmount.subtract(vipServiceConfig.getDiscountPrice());
            //  实际金额:
            orderAmount = vipServiceConfig.getDiscountPrice();

            //  订单明细:
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemName(vipServiceConfig.getName());
            orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
            orderDetailVo.setItemId(vipServiceConfig.getId());
            orderDetailVo.setItemPrice(orderAmount);
            orderDetailVoList.add(orderDetailVo);
            //  减免明细:
            if (derateAmount.compareTo(new BigDecimal("0.00")) == 1) {
                //  创建折扣对象;
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
                orderDerateVo.setRemarks("VIP折扣");
                orderDerateVoList.add(orderDerateVo);
            }
        }
        //  声明一个交易编号;
        String tradeNo = UUID.randomUUID().toString().replaceAll("-", "");
        //        String tradeNoKey = "tradeNo:" + userId + tradeNo;
        //        //  利用交易编号；防止用户无刷新重复回退提交订单！setnx + setex;
        //        this.redisTemplate.opsForValue().setIfAbsent(tradeNoKey, tradeNo, 15, TimeUnit.MINUTES);
        orderInfoVo.setTradeNo(tradeNo);
        orderInfoVo.setPayWay("");
        orderInfoVo.setItemType(tradeVo.getItemType());
        orderInfoVo.setOrderAmount(orderAmount);
        orderInfoVo.setOriginalAmount(originalAmount);
        orderInfoVo.setDerateAmount(derateAmount);
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);
        orderInfoVo.setTimestamp(SignHelper.getTimestamp());
        //  这个属性是校验签名; 防止用户篡改订单数据+接口安全性！ -- 将页面显示的所有详细信息；orderInfoVo--> 转换为Map; 当提交订单的时候，校验数据是否正确！
        //  除去sign属性！ 其他的属性值使用|进行拼接！然后再加盐！最后使用MD5加密得到的字符串!
        String sign = SignHelper.getSign(JSON.parseObject(JSON.toJSONString(orderInfoVo), Map.class));
        orderInfoVo.setSign(sign);
        //  返回数据
        return orderInfoVo;
    }
}
